package net.sf.odinms.exttools.doclet.pages;

import java.io.IOException;
import java.util.*;
import com.sun.javadoc.*;
import com.sun.tools.doclets.formats.html.LinkInfoImpl;
import com.sun.tools.doclets.internal.toolkit.util.DirectoryManager;
import com.sun.tools.doclets.internal.toolkit.util.DocletConstants;
import com.sun.tools.doclets.internal.toolkit.util.Util;
import net.sf.odinms.exttools.doclet.AbstractPageWriter;
import net.sf.odinms.exttools.doclet.Doclet;

/**
 * Creates "{packagepath}/{MemberName}.html" for classes, interfaces, etc.
 */
@SuppressWarnings("unchecked")
public final class ClassSummary extends AbstractPageWriter {
	
	/** Localized section header strings, fetched once for optimization. */
	private static enum ClassType {
		CLASS(CONF.propertyText("Class")),
		INTERFACE(CONF.propertyText("Interface")),
		ENUM(CONF.propertyText("Enum")),
		EXCEPTION(CONF.propertyText("Exception")),
		ERROR(CONF.propertyText("Error")),
		ANNOTATION(CONF.propertyText("Annotation_Type"));
		
		private String type;
		
		/**
		 * Private constructor for enum values.
		 * 
		 * @param type The localized string to use for a class type.
		 */
		private ClassType(String type) {
			this.type = type;
		}
		
		/**
		 * Method to easily fetch enum String values.
		 * 
		 * @return The type for a specific enum value.
		 */
		public String toString() {
			return type;
		}
	}
	
	/** Localized strings for heading sub-sections. */
	private static enum Heading {
		DECLARATION(CONF.propertyText("heading.Declaration")),
		ENCLOSING_CLASS(CONF.propertyText("heading.EnclosingClass")),
		ENCLOSING_INTERFACE(CONF.propertyText("heading.EnclosingInterface")),
		HIERARCHY(CONF.propertyText("heading.Hierarchy")),
		IMPLEMENTED_INTERFACES(
				CONF.propertyText("heading.ImplementedInterfaces")),
		IMPLEMENTING_CLASSES(CONF.propertyText("heading.ImplementingClasses")),
		KNOWN_DIRECT_SUBCLASSES(
				CONF.propertyText("heading.KnownDirectSubclasses")),
		SUPERINTERFACES(CONF.propertyText("heading.Superinterfaces")),
		SUBINTERFACES(CONF.propertyText("heading.Subinterfaces")),
		TYPE_PARAMETERS(CONF.propertyText("heading.TypeParameters")),
		DEPRECATED(CONF.propertyText("heading.Deprecated"));
		
		private String heading;
		
		/**
		 * Private constructor for enum values.
		 * 
		 * @param heading The localized string to use for a heading.
		 */
		private Heading(String heading) {
			this.heading = heading;
		}
		
		/**
		 * Method to easily fetch enum String values.
		 * 
		 * @return The heading for a specific enum value.
		 */
		public String toString() {
			return heading;
		}
	}
	
	/** Localized strings for member summary sections. */
	private static enum Summary {
		INNER_CLASS(CONF.propertyText("summary.InnerClass")), CONSTRUCTOR(
				CONF.propertyText("summary.Constructor")), FIELD(
				CONF.propertyText("summary.Field")), METHOD(
				CONF.propertyText("summary.Method")), ENUM_CONSTANT(
				CONF.propertyText("summary.EnumConstant"));
		
		private String summary;
		
		/**
		 * Private constructor for enum values.
		 * 
		 * @param summary The localized string to use for a summary header.
		 */
		private Summary(String summary) {
			this.summary = summary;
		}
		
		/**
		 * Method to easily fetch enum String values.
		 * 
		 * @return The summary header for a specific enum value.
		 */
		public String toString() {
			return summary;
		}
	}
	
	/** Localized strings for member detail sections. */
	private static enum Detail {
		CONSTRUCTOR(CONF.propertyText("detail.Constructor")), FIELD(
				CONF.propertyText("detail.Field")), METHOD(
				CONF.propertyText("detail.Method")), ENUM_CONSTANT(
				CONF.propertyText("detail.EnumConstant"));
		
		private String detail;
		
		/**
		 * Private constructor for enum values.
		 * 
		 * @param detail The localized string to use for a detail header.
		 */
		private Detail(String detail) {
			this.detail = detail;
		}
		
		/**
		 * Method to easily fetch enum String values.
		 * 
		 * @return The detail header for a specific enum value.
		 */
		public String toString() {
			return detail;
		}
	}
	
	private static final String EXTENDS = CONF.propertyText("extends");
	
	private ClassDoc classDoc;
	private ClassDoc prev;
	private ClassDoc next;
	
	private String pkgName;
	private String className;
	private ClassType classType;
	
	private ClassDoc[] innerClasses;
	private ConstructorDoc[] constructors;
	private MethodDoc[] methods;
	private FieldDoc[] fields;
	private FieldDoc[] enumConstants;
	private boolean hasNonPublicConstructor = false;
	
	private ClassDoc superClass;
	
	/**
	 * Generate a summary page for the the designated class/interface.
	 * 
	 * @param classDoc The class for which to create a summary
	 * @param prev The previous class in the package's alpha-ordered list.
	 * @param next The following class in the package's alpha-ordered list.
	 */
	public static void generateClassSummary(ClassDoc classDoc, ClassDoc prev,
			ClassDoc next) {
		String filename = classDoc.name() + CONF.ext;
		String path =
				DirectoryManager.getDirectoryPath(classDoc.containingPackage());
		try {
			new ClassSummary(filename, path, classDoc, prev, next);
		}
		catch (IOException exc) {
			throw Doclet.exception(exc, filename);
		}
	}
	
	/**
	 * For use only by the public static generate(...) method.
	 * 
	 * @param filename The desired name of the file (with extension).
	 * @param pathToFile The path from the document root to this file.
	 * @param classDoc The class element to be documented.
	 * @param prevClass The class that precedes this one in the same package.
	 * @param nextClass The class that follows this one in the same package.
	 * @throws IOException If {@link java.io.FileOutputStream} creation fails.
	 */
	private ClassSummary(String filename, String pathToFile, ClassDoc classDoc,
			ClassDoc prevClass, ClassDoc nextClass) throws IOException {
		super(filename, pathToFile);
		CONF.currentPkg = classDoc.containingPackage();
		CONF.currentClass = classDoc;
		pkgName = classDoc.containingPackage().name();
		className = classDoc.name();
		this.classDoc = classDoc;
		this.prev = prevClass;
		this.next = nextClass;
		
		windowTitle = className;
		// pkgName = (classDoc.containingPackage() != null) ? classDoc
		// .containingPackage().name() : "";
		
		if (classDoc.isInterface()) {
			classType = ClassType.INTERFACE;
			pageType = PageType.INTERFACE;
		} else if (classDoc.isAnnotationType()) {
			classType = ClassType.ANNOTATION;
			pageType = PageType.ANNOTATION;
		} else if (classDoc.isEnum()) {
			classType = ClassType.ENUM;
			pageType = PageType.ENUM;
		} else if (classDoc.isException()) {
			classType = ClassType.EXCEPTION;
			pageType = PageType.EXCEPTION;
		} else if (classDoc.isError()) {
			classType = ClassType.ERROR;
			pageType = PageType.ERROR;
		} else if (classDoc.isClass()) {
			classType = ClassType.CLASS;
			pageType = PageType.CLASS;
		}
		
		innerClasses = classDoc.isIncluded() ? classDoc.innerClasses() : null;
		// : (FieldDoc[]) CONF.classDocCatalog.innerClasses(className);
		enumConstants = classDoc.isIncluded() ? classDoc.enumConstants() : null;
		// : (FieldDoc[]) CONF.classDocCatalog.enums(className);
		fields = classDoc.isIncluded() ? classDoc.fields() : null;
		// : (FieldDoc[]) CONF.classDocCatalog.fields(className);
		constructors = classDoc.isIncluded() ? classDoc.constructors() : null;
		// : (FieldDoc[]) CONF.classDocCatalog.enums(className);
		methods = classDoc.isIncluded() ? classDoc.methods() : null;
		// : (FieldDoc[]) CONF.classDocCatalog.methods(className);
		superClass =
				((classDoc.isIncluded()) && (classDoc.superclassType() != null))
						? classDoc.superclassType().asClassDoc()
						: null;
		
		for (ProgramElementDoc ped : constructors) {
			if (ped.isProtected() || ped.isPrivate()) {
				hasNonPublicConstructor = true;
			}
		}
		
		printXhtmlHeader(CONF.metakeywords.getMetaKeywords(classDoc), true,
				null);
		
		printEntityCaptionPackage(classDoc, classType.type,
				classDoc.containingPackage().name());
		
		println(openDivWithID("Overview"));
		
		println(open("dl"));
		headingDeclaration();
		headingHierarchy();
		printEnclosingClass();
		headingTypeParameters();
		if (classDoc.isInterface()) {
			headingSuperinterfaces();
			headingSubinterfaces();
			headingImplementingClasses();
		}
		if (!classDoc.isInterface()) {
			headingImplementedInterfaces();
			headingKnownDirectSubclasses();
		}
		headingDeprecatedInfo();
		headingAdditionalTags();
		println(close("dl"));
		
		headingDescription();
		println(close("div") + getComment("Overview"));
		
		if (!(innerClasses.length < 1 && constructors.length < 1
				&& methods.length < 1 && fields.length < 1 && enumConstants.length < 1)) {
			println(openDivWithID("Summaries"));
			summaryInnerClasses();
			summaryEnumConstants();
			summaryFields();
			summaryConstructors();
			summaryMethods();
			println(close("div") + getComment("Summaries"));
			
			println(openDivWithID("Details"));
			detailEnumConstants();
			detailFields();
			detailConstructors();
			detailMethods();
			println(close("div") + getComment("Details"));
		}
		
		printXhtmlFooter();
		this.close();
	}
	
	/**
	 * Print "Deprecated" information (if exists) for class heading.
	 */
	private void headingDeprecatedInfo() {
		if (Util.isDeprecated(classDoc)) {
			Tag[] deprs = classDoc.tags("deprecated");
			println(open("dt id=\"Deprecated\"") + Heading.DEPRECATED
					+ close("dt"));
			if (deprs.length > 0) {
				Tag[] commentTags = deprs[0].inlineTags();
				String deprecationComments = "";
				if (commentTags.length > 0) {
					deprecationComments =
							commentTagsToString(null, classDoc,
									deprs[0].inlineTags(), false);
				} else {
					deprecationComments =
							"(No explanation given.)" + close("dd");
				}
				println(open("dd class=\"deprecated\"") + deprecationComments
						+ close("dd"));
			}
		}
		
	}
	
	/**
	 * Print class signature in "Delaration" section of the class heading.
	 */
	private void headingDeclaration() {
		println(open("dt") + Heading.DECLARATION + close("dt"));
		
		StringBuffer modifiers = new StringBuffer(classDoc.modifiers() + " ");
		if (classDoc.isEnum()) {
			modifiers.append("enum ");
			int index;
			index = modifiers.indexOf("abstract");
			if (index >= 0) {
				modifiers.delete(index, index + "abstract".length());
			}
			index = modifiers.indexOf("final");
			if (index >= 0) {
				modifiers.delete(index, index + "final".length());
			}
			modifiers =
					new StringBuffer(Util.replaceText(modifiers.toString(),
							"  ", " "));
		} else if (!classDoc.isInterface()) {
			modifiers.append("class ");
		}
		
		printDeclarationAnnotationInfo(classDoc, 0, true);
		
		print(open("dd class=\"code\"") + modifiers.toString());
		print(bold(className + typeParameters(classDoc)));
		println(close("dd"));
		
		boolean isInterface = classDoc.isInterface();
		if (!isInterface) {
			// Type superclass = Util.getFirstVisibleSuperClass(classDoc, CONF);
			ClassDoc superclass =
					Util.getFirstVisibleSuperClass(classDoc, CONF).asClassDoc();
			if (superclass != null) {
				String superclassLink =
						linkToLabelHref(escapeHTML(superclass.name()),
								hrefToDoc(superclass))
								+ typeParameters(superclass);
				println(open("dd class=\"code\"") + "extends " + superclassLink
						+ close("dd"));
			}
		}
		Type[] implementedInterfaces = classDoc.interfaceTypes();
		if (implementedInterfaces != null && implementedInterfaces.length > 0) {
			print(open("dd class=\"code\""));
			int counter = 0;
			for (Type type : implementedInterfaces) {
				ClassDoc c = type.asClassDoc();
				if (!(c.isPublic() || Util.isLinkable(c, CONF))) {
					continue;
				}
				if (counter++ == 0) {
					print(isInterface ? "extends " : "implements ");
				} else {
					print(", ");
				}
				print(linkToLabelHref(c.name(), hrefToDoc(c))
						+ typeParameters(c));
			}
			print(close("dd"));
		}
	}
	
	/**
	 * Print the class description (from Javadoc comments) for class heading.
	 */
	private void headingDescription() {
		if (!CONF.nocomment) {
			
			// generate documentation for the class.
			if (classDoc.inlineTags().length > 0) {
				println(openDivWithID("Description"));
				print(inlineComment(classDoc));
				println(close("div") + getComment("Description"));
			}
		}
	}
	
	/**
	 * Print the "Hierarchy" inheritance tree for the class heading.
	 */
	private void headingHierarchy() {
		if (!classDoc.isClass())
			return;
		println(open("dt") + Heading.HIERARCHY + close("dt"));
		// The pre is for indentation, the span is just for a valid container.
		print(open("dd") + open("pre") + open("span"));
		printTreeForClassHelper(classDoc);
		println(close("span") + close("pre") + close("dd"));
	}
	
	/**
	 * Print "All Superinterfaces" section in class heading.
	 */
	private void headingSuperinterfaces() {
		List<Type> interfaces = Util.getAllInterfaces(classDoc, CONF);
		if (classDoc.isInterface() && interfaces.size() > 0) {
			println(open("dt") + Heading.SUPERINTERFACES + close("dt"));
			println(open("dd") + open("ul"));
			for (Type type : interfaces)
				println(open("li") + linkToClass(type.asClassDoc())
						+ typeParameters(type.asClassDoc()) + close("li"));
			println(close("ul") + close("dd"));
		}
		
	}
	
	/**
	 * Print "All Known Subinterfaces" section in class heading.
	 */
	private void headingSubinterfaces() {
		List<Type> subInterfaces = CONF.classTree.allSubs(classDoc, false);
		if (classDoc.isInterface() && subInterfaces.size() > 0) {
			println(open("dt") + Heading.SUBINTERFACES + close("dt"));
			println(open("dd") + open("ul"));
			for (Type type : subInterfaces)
				println(open("li") + linkToClass(type.asClassDoc())
						+ typeParameters(type.asClassDoc()) + close("li"));
			println(close("ul") + close("dd"));
		}
	}
	
	/**
	 * Print "Known Implementing Classes" section in class heading.
	 */
	private void headingImplementingClasses() {
		if (!classDoc.isInterface()) {
			return;
		}
		if (classDoc.qualifiedName().equals("java.lang.Cloneable")
				|| classDoc.qualifiedName().equals("java.io.Serializable")) {
			return; // Don't generate the list, too big
		}
		List<ClassDoc> implementingClasses =
				CONF.classTree.implementingclasses(classDoc);
		if (implementingClasses.size() > 0) {
			println(open("dt") + Heading.IMPLEMENTING_CLASSES + close("dt"));
			println(open("dd") + open("ul"));
			for (ClassDoc doc : implementingClasses)
				println(open("li") + linkToClass(doc.asClassDoc())
						+ typeParameters(doc.asClassDoc()) + close("li"));
			println(close("ul") + close("dd"));
		}
	}
	
	/**
	 * Print "All Implemented Interfaces" section in class heading.
	 */
	private void headingImplementedInterfaces() {
		List<Type> interfaces = Util.getAllInterfaces(classDoc, CONF);
		if (classDoc.isClass() && interfaces.size() > 0) {
			println(open("dt") + Heading.IMPLEMENTED_INTERFACES + close("dt"));
			println(open("dd") + open("ul"));
			for (Type type : interfaces)
				println(open("li") + linkToClass(type.asClassDoc())
						+ typeParameters(type.asClassDoc()) + close("li"));
			println(close("ul") + close("dd"));
		}
	}
	
	/**
	 * Print "Known Direct Subclasses" section in class heading.
	 */
	private void headingKnownDirectSubclasses() {
		if (classDoc.isClass()
				&& CONF.classTree.subs(classDoc, false).size() > 0) {
			if (classDoc.qualifiedName().equals("java.lang.Object")
					|| classDoc.qualifiedName().equals("org.omg.CORBA.Object")) {
				return; // Don't generate the list, too huge
			}
			println(open("dt") + Heading.KNOWN_DIRECT_SUBCLASSES + close("dt"));
			List<ClassDoc> subclasses = CONF.classTree.subs(classDoc, false);
			println(open("dd") + open("ul"));
			for (ClassDoc doc : subclasses)
				println(open("li") + linkToClass(doc) + close("li"));
			println(close("ul") + close("dd"));
		}
	}
	
	/**
	 * Print "Type Parameters" section for class heading.
	 */
	private void headingTypeParameters() {
		if (classDoc.typeParamTags().length > 0) {
			println(open("dt") + Heading.TYPE_PARAMETERS + close("dt"));
			for (ParamTag tag : classDoc.typeParamTags()) {
				println(open("dd") + open("code") + tag.parameterName()
						+ close("code") + " - " + tag.parameterComment()
						+ close("dd"));
			}
		}
	}
	
	/**
	 * Print additional tags (&#64;since, &#64;version, &#64;author) specified
	 * in Javadoc comments.
	 */
	private void headingAdditionalTags() {
		if (classDoc.tags("author").length > 0) {
			for (Tag authorTag : classDoc.tags("author")) {
				println(open("dt") + CONF.propertyText("detail.Author")
						+ close("dt"));
				println(open("dd") + open("ul"));
				println(open("li") + authorTag.text() + close("li"));
				println(close("ul") + close("dd"));
			}
		}
		
		if (classDoc.tags("since").length > 0) {
			for (Tag sinceTag : classDoc.tags("since")) {
				println(open("dt") + CONF.propertyText("detail.Since")
						+ close("dt"));
				println(open("dd") + open("ul"));
				println(open("li") + sinceTag.text() + close("li"));
				println(close("ul") + close("dd"));
			}
		}
		
		if (classDoc.tags("version").length > 0) {
			for (Tag versionTag : classDoc.tags("since")) {
				println(open("dt") + CONF.propertyText("detail.Version")
						+ close("dt"));
				println(open("dd") + open("ul"));
				println(open("li") + versionTag.text() + close("li"));
				println(close("ul") + close("dd"));
			}
		}
	}
	
	private final int spacesPerTab = 4;
	
	/**
	 * Recursively prints the class hierarchy tree for the given class.
	 * 
	 * @param type The class to print the hierarchy for.
	 * @return The indentation amount for the next level.
	 */
	private int printTreeForClassHelper(Type type) {
		Type sup =
				Util.getFirstVisibleSuperClass(type instanceof ClassDoc
						? (ClassDoc) type
						: type.asClassDoc(), CONF);
		int indent = 0;
		if (sup != null) {
			indent = printTreeForClassHelper(sup); // Call next step
			// recursively
			println();
			print(spaces((spacesPerTab * indent) - 2, false));
			print(imgSrcAlt(pathToDocRoot + "image-files/inherit.gif", EXTENDS));
		}
		
		if (type.equals(classDoc)) {
			typeParameters(classDoc);
			String endClass = "";
			if (CONF.shouldExcludeQualifier(classDoc.containingPackage().name())) {
				endClass = type.asClassDoc().name();
			} else {
				endClass = type.asClassDoc().qualifiedName();
			}
			print(bold(endClass + typeParameters(classDoc)));
		} else {
			print(linkToClass(type.asClassDoc(), true));
		}
		return indent + 1;
	}
	
	/**
	 * Write the annotation types for the given doc, if any exist. If there are
	 * no annotation for the specified class, no elements are printed.
	 * 
	 * @param cd The {@link ClassDoc} to write annotations for.
	 * @param indent The number of extra spaces to indent the annotations.
	 * @param lineBreak Whether to add a line break between member values.
	 */
	private void printDeclarationAnnotationInfo(ClassDoc cd, int indent,
			boolean lineBreak) {
		List annotations = getAnnotations(indent, cd.annotations(), lineBreak);
		if (annotations.size() > 0) {
			for (Iterator iter = annotations.iterator(); iter.hasNext();) {
				print(open("dd class=\"code\"") + (String) iter.next()
						+ close("dd"));
			}
		}
	}
	
	/**
	 * Return the string representations of the annotation types for the given
	 * doc.
	 * 
	 * @param indent The number of extra spaces to indent the annotations.
	 * @param descList The array of {@link AnnotationDesc}.
	 * @param linkBreak If true, add new line between each member value.
	 * @return An array of strings representing the annotations being
	 *         documented.
	 */
	private List getAnnotations(int indent, AnnotationDesc[] descList,
			boolean linkBreak) {
		List<String> results = new ArrayList<String>();
		StringBuffer annotation;
		for (int i = 0; i < descList.length; i++) {
			AnnotationTypeDoc annotationDoc = descList[i].annotationType();
			if (!Util.isDocumentedAnnotation(annotationDoc)) {
				continue;
			}
			annotation = new StringBuffer();
			annotation.append(linkToClass(annotationDoc));
			AnnotationDesc.ElementValuePair[] pairs =
					descList[i].elementValues();
			if (pairs.length > 0) {
				annotation.append('(');
				for (int j = 0; j < pairs.length; j++) {
					if (j > 0) {
						annotation.append(",");
						if (linkBreak) {
							annotation.append(DocletConstants.NL);
							int spaces = annotationDoc.name().length() + 2;
							for (int k = 0; k < (spaces + indent); k++) {
								annotation.append(' ');
							}
						}
					}
					annotation.append(getDocLink(
							LinkInfoImpl.CONTEXT_ANNOTATION,
							pairs[j].element(), pairs[j].element().name(),
							false));
					annotation.append('=');
					AnnotationValue annotationValue = pairs[j].value();
					List<AnnotationValue> annotationTypeValues =
							new ArrayList<AnnotationValue>();
					if (annotationValue.value() instanceof AnnotationValue[]) {
						AnnotationValue[] annotationArray =
								(AnnotationValue[]) annotationValue.value();
						for (int k = 0; k < annotationArray.length; k++) {
							annotationTypeValues.add(annotationArray[k]);
						}
					} else {
						annotationTypeValues.add(annotationValue);
					}
					annotation.append(annotationTypeValues.size() == 1
							? ""
							: "{");
					for (Iterator iter = annotationTypeValues.iterator(); iter.hasNext();) {
						annotation.append(annotationValueToString((AnnotationValue) iter.next()));
						annotation.append(iter.hasNext() ? "," : "");
					}
					annotation.append(annotationTypeValues.size() == 1
							? ""
							: "}");
				}
				annotation.append(")");
			}
			annotation.append(linkBreak ? DocletConstants.NL : "");
			results.add(annotation.toString());
		}
		return results;
	}
	
	/**
	 * 
	 * @param annotationValue The value of an annotation type element.
	 * @return A string representation&mdash;often including a hypertext
	 *         link&mdash;for an annotation type element's value.
	 */
	private String annotationValueToString(AnnotationValue annotationValue) {
		Object value = annotationValue.value();
		if (value instanceof Type) {
			Type type = (Type) annotationValue.value();
			if (type.asClassDoc() != null) {
				
				String label =
						(type.asClassDoc().isIncluded()
								? type.typeName()
								: type.qualifiedTypeName())
								+ type.dimension() + ".class";
				return linkToLabelHref(label, linkToClass(type.asClassDoc()));
			} else {
				return type.typeName() + type.dimension() + ".class";
			}
		} else if (value instanceof AnnotationDesc) {
			List list =
					getAnnotations(0,
							new AnnotationDesc[] {(AnnotationDesc) value},
							false);
			StringBuffer buf = new StringBuffer();
			for (Iterator iter = list.iterator(); iter.hasNext();) {
				buf.append(iter.next());
			}
			return buf.toString();
		} else if (value instanceof FieldDoc) {
			return getDocLink(LinkInfoImpl.CONTEXT_ANNOTATION,
					(FieldDoc) value, ((FieldDoc) value).name(), false);
		} else {
			return annotationValue.toString();
		}
	}
	
	/**
	 * Print "Enclosing Class" section in class heading.
	 */
	private void printEnclosingClass() {
		ClassDoc outerClass = classDoc.containingClass();
		if (outerClass != null) {
			if (outerClass.isInterface())
				println(open("dt") + Heading.ENCLOSING_INTERFACE + close("dt"));
			else
				println(open("dt") + Heading.ENCLOSING_CLASS + close("dt"));
			
			println(open("dd") + linkToClass(outerClass)
					+ typeParameters(outerClass) + close("dd"));
		}
		
	}
	
	/**
	 * Print the Inner Class Summary section, including inherited inner classes.
	 */
	private void summaryInnerClasses() {
		if (innerClasses.length == 0)
			return;
		println(openDivWithID("Inner-Class-Summary"));
		println(open("h2") + Summary.INNER_CLASS + close("h2"));
		println(open("table"));
		for (ClassDoc c : new TreeSet<ClassDoc>(Arrays.asList(innerClasses))) {
			println(open("tr"));
			printTH((modifiersString(c) + classFlavor(c, true)).replaceAll(
					"interface class", ""));
			printTD(linkToClass(c));
			println(close("tr"));
		}
		// TODO Print inherited inner classes
		println(close("table"));
		println(close("div") + getComment("Inner-Class-Summary"));
	}
	
	/**
	 * Print the Constructor Summary section.
	 */
	private void summaryConstructors() {
		if (constructors.length == 0)
			return;
		println(openDivWithID("Constructor-Summary"));
		println(open("h2") + Summary.CONSTRUCTOR + close("h2"));
		println(open("table"));
		for (ConstructorDoc c : new TreeSet<ConstructorDoc>(
				Arrays.asList(constructors))) {
			println(open("tr"));
			if (hasNonPublicConstructor)
				printTH(modifiersString(c));
			// printTD(getDefinitionList(bold(getSignatureLink(c))
			// + getJoinedParameters(c, true, false, true),
			// getCommentSummary(c)));
			printTD(definitionList(
					bold(linkToLabelHref(c.name(), hrefToDoc(c)))
							+ getJoinedParameters(c, true, false, true),
					getCommentSummary(c)));
			println(close("tr"));
		}
		println(close("table"));
		println(close("div") + getComment("Constructor-Summary"));
	}
	
	/**
	 * Print the Constructor Detail section.
	 */
	private void detailConstructors() {
		if (constructors.length == 0)
			return;
		println(openDivWithID("Constructor-Detail"));
		println(open("h2") + Detail.CONSTRUCTOR + close("h2"));
		
		for (ConstructorDoc c : constructors) {
			println(openDivWithID(memberAnchor(c)));
			println(open("h3") + c.name() + close("h3"));
			
			String modifiers = modifiersString(c, true);
			int indent = (modifiers + c.name()).length() + 1;
			String exceptions =
					getJoinedStrings(getExceptionStrings(c, true), ",\n"
							+ spaces(indent, false));
			
			print(open("pre")
					+ modifiers
					+ bold(c.name())
					+ getJoinedParameters(c, true, false, true, ",\n"
							+ spaces(indent, false)));
			
			if (exceptions != null)
				print("\n"
						+ spaces(indent - "throws ".length(), false)
						+ "throws "
						+ getJoinedStrings(getExceptionStrings(c, true), ",\n"
								+ spaces(indent, false)));
			println(close("pre"));

			printItemDetail(c);
			
			println(close("div"));
		}
		
		println(close("div") + getComment("Constructor-Detail"));
	}
	
	/**
	 * Print the Method Summary section, including inherited fields.
	 */
	private void summaryMethods() {
		if (methods.length == 0)
			return;
		println(openDivWithID("Method-Summary"));
		println(open("h2") + Summary.METHOD + close("h2"));
		println(open("table"));
		// This sorts the methods just for the summary
		for (MethodDoc m : new TreeSet<MethodDoc>(Arrays.asList(methods))) {
			println(open("tr"));
			printTH(modifiersString(m) + linkToType(m));
			// printTableDataCell(bold(getSignatureLink(m)) +
			// getJoinedParameters(m.parameters()));
			
			// printTD(getJoinedStrings(getParameterStrings(m, true, false,
			// false), ", "));
			
			printTD(definitionList(bold(signatureLink(m))
					+ getJoinedParameters(m, true, false, true),
					getCommentSummary(m)));
			println(close("tr"));
		}
		// TODO Print inherited methods
		// <h3>Methods inherited from class ___<a href="" title="class in
		// ___">___</a></h3>
		// <ul>
		// <li><a href="">name</a></li>
		// </ul>
		
		println(close("table"));
		println(close("div") + getComment("Method-Summary"));
	}
	
	/**
	 * Print the Method Detail section.
	 */
	private void detailMethods() {
		if (methods.length == 0)
			return;
		println(openDivWithID("Method-Detail"));
		println(open("h2") + Detail.METHOD + close("h2"));
		
		for (MethodDoc m : methods) {
			println(openDivWithID(memberAnchor(m)));
			println(open("h3") + m.name() + close("h3"));
			
			String modifiers = modifiersString(m, true);
			int indent =
					(modifiers + getTypeString(m, false, false) + m.name()).length() + 2;
			String exceptions =
					getJoinedStrings(getExceptionStrings(m, true), ",\n"
							+ spaces(indent, false));
			
			print(open("pre")
					+ modifiers
					+ getTypeString(m, true, false)
					+ " "
					+ bold(m.name())
					+ getJoinedParameters(m, true, false, true, ",\n"
							+ spaces(indent, false)));
			
			if (exceptions != null)
				print("\n"
						+ spaces(indent - "throws ".length(), false)
						+ "throws "
						+ getJoinedStrings(getExceptionStrings(m, true), ",\n"
								+ spaces(indent, false)));
			println(close("pre"));

			printItemDetail(m);
			
			println(close("div"));
		}
		
		println(close("div") + getComment("Method-Detail"));
	}
	
	/**
	 * Print the Field Summary section, including inherited fields.
	 */
	private void summaryFields() {
		if (fields.length == 0)
			return;
		// TODO Determine how to return if no local or inherited fields, etc.
		println(openDivWithID("Field-Summary"));
		println(open("h2") + Summary.FIELD + close("h2"));
		
		println(open("table"));
		String name;
		for (FieldDoc f : new TreeSet<FieldDoc>(Arrays.asList(fields))) {
			name = f.name();
			println(open("tr"));
			printTH(modifiersString(f) + linkToType(f));
			printTD(definitionList(bold(linkToLabelHref(name, "#" + name)),
					getCommentSummary(f)));
			println(close("tr"));
		}
		// TODO Print inherited fields
		
		println(close("table"));
		println(close("div") + getComment("Field-Summary"));
	}
	
	/**
	 * Print the Field Detail section.
	 */
	private void detailFields() {
		if (fields.length == 0)
			return;
		println(openDivWithID("Field-Detail"));
		println(open("h2") + Detail.FIELD + close("h2"));
		for (FieldDoc f : fields) {
			println(openDivWithID(f.name()));
			println(open("h3") + f.name() + close("h3"));
			
			println(open("pre") + f.modifiers() + " " + linkToType(f.type())
					+ " " + bold(f.name()) + close("pre"));
			println(open("dl"));
			
			printItemDetail(f);
			
			if (f.constantValueExpression() != null) {
				printDT("See Also:");
				printDD(linkToLabelHref(CONF.getText("doclet.Constant_Field_Values"), pathToDocRoot
						+ "constant-values" + CONF.ext + "#"
						+ f.qualifiedName()));
			}
			println(close("dl"));
			
			println(close("div"));
		}
		println(close("div") + getComment("Field-Detail"));
	}
	
	/**
	 * Print the Enum Constant Summary section.
	 */
	private void summaryEnumConstants() {
		if (enumConstants.length == 0)
			return;
		println(openDivWithID("Enum-Constant-Summary"));
		println(open("h2") + Summary.ENUM_CONSTANT + close("h2"));
		println(open("table"));
		for (FieldDoc ec : new TreeSet<FieldDoc>(Arrays.asList(enumConstants))) {
			println(open("tr"));
			printTD(linkToLabelHref(ec.name(), "#" + ec.name()));
			// TODO Print matching constant enum declarations?
			println(close("tr"));
		}
		// TODO Print inherited enums constants?
		println(close("table"));
		println(close("div") + getComment("Enum-Constant-Summary"));
	}
	
	/**
	 * Print the Enum Constant Detail section.
	 */
	private void detailEnumConstants() {
		if (enumConstants.length == 0)
			return;
		println(openDivWithID("Enum-Constant-Detail"));
		println(open("h2") + Detail.ENUM_CONSTANT + close("h2"));
		
		println(close("div") + getComment("Enum-Constant-Detail"));
	}
	
	/**
	 * Prints the fine details about an item.
	 * 
	 * I.E. Tags, etc.
	 * 
	 * @param detailDoc The item to print details about.
	 */
	private void printItemDetail(Doc detailDoc) {
		boolean doneParams = false;
		
		printDT("");
		printDD(commentTagsToString(null, detailDoc, detailDoc.inlineTags(),
				false));
		print(empty("br"));
		
		for (Tag currentTag : detailDoc.tags()) {
			String attemptedName = currentTag.name();
			if (currentTag instanceof ParamTag) { // @param needs special
				// handling.
				if (doneParams == false) {
					doneParams = true;
					printDT(CONF.propertyText("tag.Param"));
					println(open("ul"));
					for (Tag preConvTag : detailDoc.tags("param")) {
						ParamTag paramTag = (ParamTag) preConvTag;
						println(open("li") + open("code")
								+ open("span style='font-weight: normal'")
								+ paramTag.parameterName() + close("code")
								+ " - "
								+ paramTag.parameterComment() + close("span")
								+ close("li"));
					}
					println(close("ul"));
				}
				continue;
			}
			attemptedName = attemptedName.replaceFirst("@", "");
			attemptedName =
					Character.toUpperCase(attemptedName.charAt(0))
							+ attemptedName.substring(1);
			try {
				attemptedName = CONF.propertyText("tag." + attemptedName);
				printDT(attemptedName);
				printDD(commentTagsToString(currentTag, detailDoc,
						currentTag.inlineTags(), false));
			}
			catch (MissingResourceException mre) {
				// Probably an unsupported tag. But still, check if it's
				// ignored...
				try {
					CONF.propertyText("ignore." + mre.getKey());
				}
				catch (MissingResourceException mre2) {
					// Definitely unsupported - write something to the
					// console...
					System.out.print("Found unsupported tag: ");
					System.out.print(mre.getKey());
					System.out.println();
				}
			}
			
		}
	}
	
	/** Link "Package" to the enclosing package's summary. */
	// TODO Verify behavior for 0 or 1 packages
	protected void navLinkPackage() {
		println(listItem(linkToLabelHrefTitle(PACKAGE, "package-summary"
				+ CONF.ext, CONF.propertyText("navtitle.Package", pkgName))));
	}
	
	/** Highlight "Class" as current section, don't create link. */
	protected void navLinkClass() {
		println(listItemCurrent(CLASS));
	}
	
	/** Link "Use" to the use of the current class. */
	protected void navLinkUse() {
		println(listItem(linkToLabelHrefTitle(USE, "class-use" + SLASH
				+ filename, CONF.propertyText("navtitle.Use", classDoc.name()))));
	}
	
	/** Link "Tree" to the enclosing package's tree. */
	protected void navLinkTree() {
		println(listItem(linkToLabelHrefTitle(TREE, "package-tree" + CONF.ext,
				CONF.propertyText("navtitle.Tree.package", pkgName))));
	}
	
	/** Print links to Summary and Detail for class elements. */
	protected void navWithinPage() {
		print(open("td id=\"WithinPage\""));
		// Links to summary sections within document
		List<String> summaries = new Vector<String>();
		if (innerClasses.length > 0)
			summaries.add(linkToLabelHref(CONF.propertyText("Inner_Classes"),
					"#Inner-Class-Summary"));
		if (constructors.length > 0)
			summaries.add(linkToLabelHref(CONF.propertyText("Constructors"),
					"#Constructor-Summary"));
		if (fields.length > 0)
			summaries.add(linkToLabelHref(CONF.propertyText("Fields"),
					"#Field-Summary"));
		if (methods.length > 0)
			summaries.add(linkToLabelHref(CONF.propertyText("Methods"),
					"#Method-Summary"));
		if (enumConstants.length > 0)
			summaries.add(linkToLabelHref(CONF.propertyText("Enums"),
					"#Enum-Summary"));
		// Links to detail sections within document
		List<String> details = new Vector<String>();
		if (constructors.length > 0)
			details.add(linkToLabelHref(CONF.propertyText("Constructors"),
					"#Constructor-Detail"));
		if (fields.length > 0)
			details.add(linkToLabelHref(CONF.propertyText("Fields"),
					"#Field-Detail"));
		if (methods.length > 0)
			details.add(linkToLabelHref(CONF.propertyText("Methods"),
					"#Method-Detail"));
		if (enumConstants.length > 0)
			details.add(linkToLabelHref(CONF.propertyText("Enums"),
					"#Enum-Detail"));
		
		printUnorderedListWithLast(summaries);
		printUnorderedListWithLast(details);
		println(close("td"));
		println(open("td class=\"labels\""));
		TagBuilder strongTag = new TagBuilder("strong");
		println(strongTag.getOpenTextClose(SUMMARY) + empty("br")
				+ strongTag.getOpenTextClose(DETAIL));
		println(close("td"));
	}
	
	/** Print links to previous and next classes, or static label if none. */
	protected void navPreviousNext() {
		println(openULWithID("PreviousNext"));
		println(listItem(linkToLabelHref(PREV_CLASS, hrefToDoc(prev)))
				+ listItemLast(linkToLabelHref(NEXT_CLASS, hrefToDoc(next))));
		println(close("ul"));
	}
	
}
